// 2024-12-10 16:54:33 Rexet
const express = require('express');
const app = express();
const { createServer } = require('node:http');
const cors = require('cors');
const { Server } = require('socket.io');
const bodyParser = require('body-parser')


const server = createServer(app);
const io = new Server(server, {
  connectionStateRecovery: {},
  cors: {
    origin: '*'
  }
});
const routes = require('./routes')

app.use(cors({
  origin: function(origin, callback) {
    callback(null, true);
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json());
app.use(express.text());
app.use(express.static('public'));
routes(app)

// let crypto = require('crypto');
// console.log(crypto.randomBytes(9).toString('hex')+ ' sessionId')


app.get('/', (req, res) => {
  res.status(200).send('yes')
});


//socket.io
const onlineUsers = new Map()

io.on('connection', (socket) => {
  console.log(socket.id + ' connect')

  socket.on('setOnline', (data)=>{
    const { uid, friendList, joinedGroup } = data
    console.log(joinedGroup)
    console.warn(`set ${uid} -> ${socket.id} online`)
    onlineUsers.set(uid, {socketID: socket.id, friendList, joinedGroup})

    joinedGroup.forEach( group => {
      console.log(group)
      if (group.group_id) {
        io.to(group.group_id).emit('update online status', {uid, isOnline: true} )
      }
    })

    friendList.forEach( friend => {
      const friendData = onlineUsers.get(friend.id)
      if (friendData) {
        io.to(friendData.socketID).emit('friendOnline', uid )
      }
    })
  })

  //有新聊天消息时
  socket.on('chat message', (data) => {
    console.log(data)
    const { chat_id } = data
    // console.table(data)
    // const chat_id = data.chat_id
    socket.broadcast.to(chat_id).emit('chat message', data)
  });

  socket.on('join chat', chatId => socket.join(chatId))
  socket.on('leave chat', chatId => socket.leave(chatId))

  socket.on('join group', gid => socket.join(gid))
  socket.on('leave group', gid => socket.leave(gid))

  socket.on('disconnect', ()=> {
    console.log(socket.id+' disconnect')
    console.table(onlineUsers)

    let offlineUserId = null
    let friendList = []
    let joinedGroup = []
    for (let [uid, data] of onlineUsers.entries()) {
      if (socket.id === data.socketID) {
        offlineUserId = uid
        joinedGroup = data.joinedGroup
        friendList = data.friendList
        onlineUsers.delete(uid)
        break;
      }
    }

    joinedGroup.forEach( group => {
      console.log(group)
      if (group.group_id) {
        io.to(group.group_id).emit('update online status', {uid: offlineUserId, isOnline: false} )
      }
    })

    friendList.forEach(friend => {
      if (onlineUsers.has(friend.id)) {
        const friendSocketID = onlineUsers.get(friend.id).socketID;
        io.to(friendSocketID).emit('friend_offline', offlineUserId);
      }
    })
    socket.emit('offline', 123)
  })

  socket.on('test', ()=>{
    console.warn('SOCKET TESTING')
  })

  socket.on('getOnlineFriends', friendList => {
    const onlineFriends = [];

    friendList.forEach( friend => {
      if (onlineUsers.has(friend.id)) {
        onlineFriends.push(friend.id)
        io.to(socket.id).emit('friendAlreadyOnline', onlineFriends);
      }
    });
  })

  socket.on('sendFriendReq', data => {
    const { receiver_id } = data
      if (onlineUsers.has(parseInt(receiver_id))) {
        const receiver_socketId = onlineUsers.get(parseInt(receiver_id)).socketID
        io.to(receiver_socketId).emit('newFriendReq', data)
      }
  })

  socket.on('friendAccepted', data => {
    const {accepter, targetUser} = data
    for (let [key, value] of onlineUsers.entries()) {
      if (targetUser.id === key) {
        io.to(value.socketID).emit('friendAccepted', accepter)
        value.friendList.push(accepter)
        if (onlineUsers.get(accepter.id)) {
          onlineUsers.get(accepter.id).friendList.push(targetUser)
        }
        break
      }
    }
  })

  socket.on('update message', data => {
    socket.broadcast.to(data.chatId).emit('update message', data)
  })

  socket.on('delete message', data => {
    console.log(data)
    socket.broadcast.to(data.chatId).emit('delete message', data)
  })

  socket.on('check online', uid => {
    console.log(uid)
    const boolean = onlineUsers.has(uid)
    socket.emit('update online status', {uid, isOnline: boolean})
  })


  //--------------------------------------------------------------------
  socket.on('offerSdp', offerSdp => {
    socket.broadcast.emit('offerSdp', offerSdp)
  })

  socket.on('answerSdp', answerSdp => {
    socket.broadcast.emit('answerSdp', answerSdp)
    console.log(answerSdp)
  })


  socket.on('icecandidate', candidate => {
    socket.broadcast.emit('icecandidate', candidate)
  })

});

server.listen(3000, () => console.log('server running at http://localhost:3000'));